Make GtkFrame allocate its label considering natural size requests
authorTristan Van Berkom <tristan.van.berkom@gmail.com>
Fri, 6 Aug 2010 19:58:12 +0000 (15:58 -0400)
committerTristan Van Berkom <tristan.van.berkom@gmail.com>
Fri, 6 Aug 2010 20:57:11 +0000 (16:57 -0400)
Also like the GtkExpander, the label widget is allocated the minimum
height for its allocated width and the remaining space is given to
the child, test case included.

gtk/gtkframe.c
tests/testheightforwidth.c

index 7f6c0e2098c7fde338b823c6bb6a9f52cd590f58..76cf1fd9587b606a8175346b17421e968cac6bc4 100644 (file)
@@ -47,6 +47,7 @@ struct _GtkFramePriv
   /* Properties */
 
   GtkAllocation child_allocation;
+  GtkAllocation label_allocation;
 };
 
 enum {
@@ -99,6 +100,15 @@ static void gtk_frame_get_width                     (GtkSizeRequest      *widget
 static void gtk_frame_get_height                    (GtkSizeRequest      *widget,
                                                     gint                *minimum_size,
                                                     gint                *natural_size);
+static void gtk_frame_get_height_for_width          (GtkSizeRequest      *layout,
+                                                    gint                 width,
+                                                    gint                *minimum_height,
+                                                    gint                *natural_height);
+static void gtk_frame_get_width_for_height          (GtkSizeRequest      *layout,
+                                                    gint                 width,
+                                                    gint                *minimum_height,
+                                                    gint                *natural_height);
+
 
 G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
@@ -597,24 +607,21 @@ gtk_frame_paint (GtkWidget    *widget,
 
       if (priv->label_widget)
        {
-         GtkRequisition child_requisition;
          gfloat xalign;
          gint height_extra;
          gint x2;
 
-         gtk_widget_get_child_requisition (priv->label_widget, &child_requisition);
-
          if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
            xalign = priv->label_xalign;
          else
            xalign = 1 - priv->label_xalign;
 
-         height_extra = MAX (0, child_requisition.height - widget->style->ythickness)
-           - priv->label_yalign * child_requisition.height;
+         height_extra = MAX (0, priv->label_allocation.height - widget->style->ythickness)
+           - priv->label_yalign * priv->label_allocation.height;
          y -= height_extra;
          height += height_extra;
          
-         x2 = widget->style->xthickness + (priv->child_allocation.width - child_requisition.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_SIDE_PAD;
+         x2 = widget->style->xthickness + (priv->child_allocation.width - priv->label_allocation.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_SIDE_PAD;
          
          /* If the label is completely over or under the frame we can omit the gap */
          if (priv->label_yalign == 0.0 || priv->label_yalign == 1.0)
@@ -628,7 +635,7 @@ gtk_frame_paint (GtkWidget    *widget,
                                  area, widget, "frame",
                                  x, y, width, height,
                                  GTK_POS_TOP,
-                                 x2, child_requisition.width + 2 * LABEL_PAD);
+                                 x2, priv->label_allocation.width + 2 * LABEL_PAD);
        }
        else
         gtk_paint_shadow (widget->style, widget->window,
@@ -688,25 +695,32 @@ gtk_frame_size_allocate (GtkWidget     *widget,
 
   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
     {
-      GtkRequisition child_requisition;
-      GtkAllocation child_allocation;
+      gint nat_width, width, height;
       gfloat xalign;
 
-      gtk_widget_get_child_requisition (priv->label_widget, &child_requisition);
-
       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
        xalign = priv->label_xalign;
       else
        xalign = 1 - priv->label_xalign;
 
-      child_allocation.x = priv->child_allocation.x + LABEL_SIDE_PAD +
-       (priv->child_allocation.width - child_requisition.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_PAD;
-      child_allocation.width = MIN (child_requisition.width, new_allocation.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD);
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->label_widget), NULL, &nat_width);
+      width = new_allocation.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD;
+      width = MIN (width, nat_width);
+
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->label_widget), width,
+                                            &height, NULL);
+
+
+      priv->label_allocation.x = priv->child_allocation.x + LABEL_SIDE_PAD +
+       (new_allocation.width - width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_PAD;
 
-      child_allocation.y = priv->child_allocation.y - MAX (child_requisition.height, widget->style->ythickness);
-      child_allocation.height = child_requisition.height;
+      priv->label_allocation.width = width;
 
-      gtk_widget_size_allocate (priv->label_widget, &child_allocation);
+
+      priv->label_allocation.y = priv->child_allocation.y - MAX (height, widget->style->ythickness);
+      priv->label_allocation.height = height;
+
+      gtk_widget_size_allocate (priv->label_widget, &priv->label_allocation);
     }
 }
 
@@ -727,19 +741,30 @@ gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
   GtkFramePriv *priv = frame->priv;
   GtkWidget *widget = GTK_WIDGET (frame);
   GtkAllocation *allocation = &widget->allocation;
-  GtkRequisition child_requisition;
   gint top_margin;
   guint border_width;
 
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));
+
   if (priv->label_widget)
     {
-      gtk_widget_get_child_requisition (priv->label_widget, &child_requisition);
-      top_margin = MAX (child_requisition.height, widget->style->ythickness);
+      gint nat_width, width, height;
+
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->label_widget), NULL, &nat_width);
+
+      width = widget->allocation.width;
+      width -= 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;
+      width -= (border_width + GTK_WIDGET (widget)->style->xthickness) * 2;
+
+      width = MIN (width, nat_width);
+
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->label_widget), width,
+                                            &height, NULL);
+
+      top_margin = MAX (height, widget->style->ythickness);
     }
   else
     top_margin = widget->style->ythickness;
-
-  border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));
   
   child_allocation->x = border_width + widget->style->xthickness;
   child_allocation->width = MAX(1, (gint)allocation->width - child_allocation->x * 2);
@@ -846,9 +871,67 @@ gtk_frame_get_height (GtkSizeRequest *widget,
   gtk_frame_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
 }
 
+
+static void
+gtk_frame_get_height_for_width (GtkSizeRequest *request,
+                               gint            width,
+                               gint           *minimum_height,
+                               gint           *natural_height)
+{
+  GtkWidget *widget = GTK_WIDGET (request);
+  GtkWidget *child;
+  GtkFrame *frame = GTK_FRAME (widget);
+  GtkFramePriv *priv = frame->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  gint child_min, child_nat, label_width;
+  gint minimum, natural;
+  guint border_width;
+
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+  minimum      = (border_width + GTK_WIDGET (widget)->style->ythickness) * 2;
+  natural      = (border_width + GTK_WIDGET (widget)->style->ythickness) * 2;
+
+  width -= (border_width + GTK_WIDGET (widget)->style->xthickness) * 2;
+  label_width = width - 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;
+
+  if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
+    {
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->label_widget),
+                                            label_width, &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->label_widget),
+                                            width, &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+ if (minimum_height)
+    *minimum_height = minimum;
+
+  if (natural_height)
+    *natural_height = natural;
+}
+
+static void
+gtk_frame_get_width_for_height (GtkSizeRequest *widget,
+                               gint       height,
+                               gint      *minimum_width,
+                               gint      *natural_width)
+{
+  gtk_size_request_get_width (widget, minimum_width, natural_width);
+}
+
 static void
 gtk_frame_size_request_init (GtkSizeRequestIface *iface)
 {
-  iface->get_width = gtk_frame_get_width;
-  iface->get_height = gtk_frame_get_height;
+  iface->get_width            = gtk_frame_get_width;
+  iface->get_height           = gtk_frame_get_height;
+  iface->get_height_for_width = gtk_frame_get_height_for_width;
+  iface->get_width_for_height = gtk_frame_get_width_for_height;
 }
index e59d3bb6f75ced71b8c42cb3af062ae034760653..f784864b6bf710a5914d9e11222e792743469d1e 100644 (file)
@@ -615,6 +615,49 @@ TestInterface interfaces[] = {
     "</interface>",
     NULL
   },
+
+  {
+    "Wrapping Frame Label",
+    "This test demonstrates how the frame label can fill to its natural width "
+    "and also trade height for width.",
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+    "<interface>"
+    "  <requires lib=\"gtk+\" version=\"2.20\"/>"
+    "  <!-- interface-naming-policy project-wide -->"
+    "  <object class=\"GtkWindow\" id=\"window\">"
+    "    <property name=\"default_width\">400</property>"
+    "    <property name=\"default_height\">150</property>"
+    "    <child>"
+    "      <object class=\"GtkFrame\" id=\"frame1\">"
+    "        <property name=\"visible\">True</property>"
+    "        <property name=\"border_width\">8</property>"
+    "        <property name=\"label_xalign\">0</property>"
+    "        <child>"
+    "          <object class=\"GtkAlignment\" id=\"alignment1\">"
+    "            <property name=\"visible\">True</property>"
+    "            <property name=\"left_padding\">12</property>"
+    "            <child>"
+    "              <object class=\"GtkLabel\" id=\"label2\">"
+    "                <property name=\"visible\">True</property>"
+    "                <property name=\"label\" translatable=\"yes\">some content</property>"
+    "              </object>"
+    "            </child>"
+    "          </object>"
+    "        </child>"
+    "        <child type=\"label\">"
+    "          <object class=\"GtkLabel\" id=\"label1\">"
+    "            <property name=\"visible\">True</property>"
+    "            <property name=\"label\" translatable=\"yes\">A frame label that's a little long and wraps</property>"
+    "            <property name=\"use_markup\">True</property>"
+    "            <property name=\"wrap\">True</property>"
+    "          </object>"
+    "        </child>"
+    "      </object>"
+    "    </child>"
+    "  </object>"
+    "</interface>",
+    NULL
+  },
 };